package com.letoken.platform.pub.util;

import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSONObject;
import com.letoken.platform.pub.enums.LetokenApiResponseEnum;
import com.letoken.platform.pub.exception.ApiServiceException;
import com.letoken.platform.pub.res.token.TokenErc20TxRes;
import com.letoken.platform.pub.res.token.TokenTxRes;
import com.letoken.platform.pub.res.web3.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.web3j.abi.TypeDecoder;
import org.web3j.abi.datatypes.generated.Uint256;

import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

/**
 * @author letokenlabs
 * @date 2022/11/18$
 */
@Slf4j
@Component
public class MoralisUtil {

    public static String url;
    public static String apikey;

    @Value("${moralis.api.endpoint:https://deep-index.moralis.io/api/v2}")
    private String _url;

    @Value("${moralis.api.apikey:}")
    private String _apikey;

    @PostConstruct
    public void init() {
        url = _url;
        apikey = _apikey;
    }

    public static String getMoralisNetwork(String netType) {
        String res = netType;
        switch (netType) {
            case "bsc":
            case "56":
                res = "0x38";
                break;
            case "bsctestnet":
            case "97":
                res = "0x61";
                break;
        }
        return res;
    }

    /**
     * 获取原始代币余额
     *
     * @param address 用户钱包地址
     * @param netType 网络名称
     * @return wei
     */
    public static String getBalance(String address, String netType) {
        StringBuffer requestUrl = new StringBuffer(url);
        //组装请求url
        requestUrl.append("/" + address).append("/balance").append("?chain=" + getMoralisNetwork(netType));
        HashMap<String, String> header = new HashMap<>();
        header.put("accept", "application/json");
        header.put("X-API-Key", apikey);
        String result = null;
        try {
            result = HttpClientUtil.sendGet(requestUrl.toString(), null, header);
        } catch (Exception e) {
            e.printStackTrace();
        }
        String balanec = null;
        if (null != result) {
            JSONObject rep = JSONObject.parseObject(result);
            balanec = rep.get("balance").toString();
        }
        return balanec;
    }


    /**
     * 根据token地址查询指定钱包token余额
     *
     * @param address
     * @param tokenAddress
     * @param netType
     * @return wei
     */
    public static String getTokenBalance(String tokenAddress, String address, String netType) {
        StringBuffer requestUrl = new StringBuffer(url);
        //组装请求url
        requestUrl.append("/" + address).append("/erc20").append("?chain=" + getMoralisNetwork(netType)).append("&token_addresses=" + tokenAddress);
        HashMap<String, String> header = new HashMap<>();
        header.put("accept", "application/json");
        header.put("X-API-Key", apikey);
        String result = null;
        try {
            result = HttpClientUtil.sendGet(requestUrl.toString(), null, header);
        } catch (Exception e) {
            e.printStackTrace();
        }
        String balanec = null;
        if (null != result) {
            List<Object> moralisBalanec = JSONObject.parseObject(result, List.class);
            MoralisBalanec moralisBalanec1 = JSONObject.parseObject(moralisBalanec.get(0).toString(), MoralisBalanec.class);
            balanec = moralisBalanec1.getBalance();
        }
        return balanec;
    }


    /**
     * 根据交易hash查询交易详情
     *
     * @param hash
     * @param netType
     * @return
     */
    public static MoralisTransaction getTransactionByHash(String hash, String netType) {
        StringBuffer requestUrl = new StringBuffer(url);
        //组装请求url
        requestUrl.append("/transaction").append("/" + hash).append("?chain=" + getMoralisNetwork(netType));
        HashMap<String, String> header = new HashMap<>();
        header.put("accept", "application/json");
        header.put("X-API-Key", apikey);
        String result = null;
        try {
            result = HttpClientUtil.sendGet(requestUrl.toString(), null, header);
        } catch (Exception e) {
            throw new ApiServiceException(LetokenApiResponseEnum.ERROR_WALLET_TX_001);
        }
        MoralisTransaction res = null;
        if (null != result) {
            res = JSONObject.parseObject(result, MoralisTransaction.class);
        }
        return res;
    }


    /**
     * 根据钱包地址查询交易详情
     *
     * @param address
     * @param netType
     * @param startTime 开始时间
     * @param endTime   结束时间
     * @return
     */
    public static List<ResultTransaction> getTokenTransactionByHash(String address, String netType, String startTime, String endTime) {
        StringBuffer requestUrl = new StringBuffer(url);
        //组装请求url
        requestUrl.append("/" + address).append("/erc20/transfers").append("?chain=" + getMoralisNetwork(netType)).append("&from_date=" + startTime).append("&to_date=" + endTime);
        HashMap<String, String> header = new HashMap<>();
        header.put("accept", "application/json");
        header.put("X-API-Key", apikey);
        String result = null;
        try {
            result = HttpClientUtil.sendGet(requestUrl.toString(), null, header);
        } catch (Exception e) {
            throw new ApiServiceException(LetokenApiResponseEnum.ERROR_WALLET_TX_001);
        }
        JSONObject ob = JSONObject.parseObject(result);
        String resultStr = null;
        if (ObjectUtil.isNotEmpty(ob)) {
            resultStr = ob.get("result").toString();
        }
        List<ResultTransaction> res = new ArrayList<>();
        if (null != resultStr) {
            List<Object> list = JSONObject.parseObject(resultStr, List.class);
            list.forEach(obj -> {
                res.add(JSONObject.parseObject(obj.toString(), ResultTransaction.class));
            });
        }
        return res;
    }


    /**
     * 根据钱包地址查询交易详情
     *
     * @param netType
     * @param blockNumberOrHash
     * @return
     */
    public static BlockTransaction getBlockTransaction(String netType, String blockNumberOrHash) {
        StringBuffer requestUrl = new StringBuffer(url);
        //组装请求url
        requestUrl.append("/block/" + blockNumberOrHash).append("?chain=" + getMoralisNetwork(netType));
        HashMap<String, String> header = new HashMap<>();
        header.put("accept", "application/json");
        header.put("X-API-Key", apikey);
        String result = null;
        try {
            result = HttpClientUtil.sendGet(requestUrl.toString(), null, header);
        } catch (Exception e) {
            throw new ApiServiceException(LetokenApiResponseEnum.ERROR_WALLET_TX_001);
        }

        BlockTransaction res = new BlockTransaction();
        if (null != result) {
            res = JSONObject.parseObject(result, BlockTransaction.class);
        }
        return res;
    }

    /**
     * 获取区块里的所有ERC20代币交易记录
     *
     * @param netType
     * @param blockNumberOrHash
     * @return
     */
    public static List<ResultTransaction> getBlockTokenTransactions(String netType, String blockNumberOrHash) {
        List<ResultTransaction> list = new ArrayList<>();
        BlockTransaction blockTransaction = getBlockTransaction(netType, blockNumberOrHash);
        if (blockTransaction == null) {
            return null;
        }
        if (blockTransaction != null && blockTransaction.getTransactions() != null &&
                !blockTransaction.getTransactions().isEmpty()) {

            blockTransaction.getTransactions().forEach(obj -> {
                if (obj.getLogs() != null && !obj.getLogs().isEmpty()) {
                    JSONObject log = obj.getLogs().get(0);
                    if (log != null && log.get("topic2") != null) {
                        ResultTransaction item = new ResultTransaction();
                        BeanUtil.quickCopy(obj, item);
                        Object topic2 = log.get("topic2");
                        if (topic2 != null && !StringUtils.isEmpty(topic2))
                            item.setReciever(TypeDecoder.decodeAddress(topic2.toString()).toString());

                        try {
                            Object data = log.get("data");
                            if (data != null && !StringUtils.isEmpty(data)) {
                                item.setValue(TypeDecoder.decode(data.toString(), Uint256.class).getValue().toString());
                            }
                        } catch (Exception ex) {
                            ex.printStackTrace();
                        }

                        list.add(item);
                    }
                }
            });
        }
        return list;
    }

    public static MoralisTokenPrice getTokenPrice(String netType, String address) {
        StringBuffer requestUrl = new StringBuffer(url);
        //组装请求url
        requestUrl.append("/erc20/").append(address).append("/price?chain=" + getMoralisNetwork(netType));
        HashMap<String, String> header = new HashMap<>();
        header.put("accept", "application/json");
        header.put("X-API-Key", apikey);
        String result = null;
        try {
            result = HttpClientUtil.sendGet(requestUrl.toString(), null, header);
        } catch (Exception e) {
            log.error("MoralisUtil getTokenPrice fail! " + e.getMessage());
            return null;
        }

        MoralisTokenPrice res = new MoralisTokenPrice();
        if (null != result) {
            res = JSONObject.parseObject(result, MoralisTokenPrice.class);
        }
        return res;
    }

    public static List<TokenErc20TxRes> getTokenErc20Txs(String netType, String contractAddress, String walletAddress, Integer page, Integer pageSize, String sort) {
        StringBuffer requestUrl = new StringBuffer(url);
        //组装请求url
        requestUrl.append("/erc20/transfers").append("?chain=" + getMoralisNetwork(netType)).append("&limit=" + pageSize).append("&contract_addresses%5B0%5D=" + contractAddress).append("&wallet_addresses%5B0%5D=" + walletAddress);

        HashMap<String, String> header = new HashMap<>();
        header.put("accept", "application/json");
        header.put("X-API-Key", apikey);
        String result = null;
        try {
            result = HttpClientUtil.sendGet(requestUrl.toString(), null, header);

            JSONObject ob = JSONObject.parseObject(result);
            String resultStr = null;
            if (ObjectUtil.isNotEmpty(ob)) {
                resultStr = ob.get("result").toString();
            }

            List<TokenErc20TxRes> list = null;

            if (null != resultStr) {
                List<MoralisErc20Transaction> txs = JSONObject.parseArray(resultStr, MoralisErc20Transaction.class);
                if (txs != null && !txs.isEmpty()) {
                    list = new ArrayList<>();
                    for (MoralisErc20Transaction tx : txs) {

                        TokenErc20TxRes item = new TokenErc20TxRes();

                        item.setBlockHash(tx.getBlockHash());
                        item.setBlockNumber(tx.getBlockNumber());
                        item.setTimeStamp(tx.getBlockTimestamp());
                        item.setHash(tx.getTransactionHash());
                        item.setFrom(tx.getFromWallet());
                        item.setTo(tx.getToWallet());
                        item.setContractAddress(contractAddress);
                        item.setTransactionIndex(tx.getLogIndex());
                        item.setValue(tx.getValue());

                        list.add(item);
                    }
                }
            }

            return list;

        } catch (Exception e) {
            log.error("MoralisUtil getTokenErc20Txs fail! " + e.getMessage());
            return null;
        }


    }

    public static List<TokenTxRes> getTokenTxs(String netType, String walletAddress, Integer page, Integer pageSize, String sort) {
        StringBuffer requestUrl = new StringBuffer(url);
        //组装请求url
        requestUrl.append("/" + walletAddress).append("?chain=" + getMoralisNetwork(netType)).append("&limit=" + pageSize);

        HashMap<String, String> header = new HashMap<>();
        header.put("accept", "application/json");
        header.put("X-API-Key", apikey);
        String result = null;
        try {
            result = HttpClientUtil.sendGet(requestUrl.toString(), null, header);

            JSONObject ob = JSONObject.parseObject(result);
            String resultStr = null;
            if (ObjectUtil.isNotEmpty(ob)) {
                resultStr = ob.get("result").toString();
            }

            List<TokenTxRes> list = null;

            if (null != resultStr) {
                List<MoralisTransaction> txs = JSONObject.parseArray(resultStr, MoralisTransaction.class);
                if (txs != null && !txs.isEmpty()) {
                    list = new ArrayList<>();
                    for (MoralisTransaction tx : txs) {

                        TokenTxRes item = new TokenTxRes();

                        item.setNonce(tx.getNonce());
                        item.setBlockHash(tx.getBlockHash());
                        item.setBlockNumber(tx.getBlockNumber());
                        item.setTimeStamp(tx.getBlockTimestamp());
                        item.setHash(tx.getHash());
                        item.setFrom(tx.getFromAddress());
                        item.setTo(tx.getToAddress());
                        item.setTxreceipt_status(tx.getReceiptStatus());
                        item.setGas(tx.getGas());
                        item.setGasPrice(tx.getGasPrice());
                        item.setTransactionIndex(tx.getTransactionIndex());
                        item.setValue(tx.getValue());

                        list.add(item);
                    }
                }
            }

            return list;

        } catch (Exception e) {
            log.error("MoralisUtil getTokenTxs fail! " + e.getMessage());
            return null;
        }
    }
}
